package com.fsh.lingsp.common.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.fsh.lingsp.common.user.dao.UserDao;
import com.fsh.lingsp.common.user.domain.entity.User;
import com.fsh.lingsp.common.user.service.UserService;
import com.fsh.lingsp.common.user.service.WXMsgService;
import com.fsh.lingsp.common.user.service.adapter.TextBuilder;
import com.fsh.lingsp.common.user.service.adapter.UserAdapter;
import com.fsh.lingsp.common.websocket.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class WXMsgServiceImpl implements WXMsgService {

    @Autowired
    private UserDao userDao;
    @Autowired
    private UserService userService;
    @Autowired
    private WebSocketService webSocketService;

    @Value("${wx.mp.callback}")
    private String callBack;

    private static final String  URL="https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STATE#wechat_redirect";

    /**
     * openId 和 登录code 的映射map ， 临时关系。
     */
    private static final ConcurrentHashMap<String,Integer> WAIT_AUTHORIZE_MAP=new ConcurrentHashMap<>();

    /**
     * 用户扫码处理（已关注和未关注）
     */
    @Override
    public WxMpXmlOutMessage scan(WxMpXmlMessage wxMpXmlMessage, WxMpService wxMpService) {
        ////用户扫完码后，就可以获取一些信息。 code、openid等
        String openId = wxMpXmlMessage.getFromUser();
        Integer code=getEventKey(wxMpXmlMessage);
        if (Objects.isNull(code)){
            return null;
        }
        //查询该用户是否已经注册过并授权过。
        User user=userDao.getByOpenId(openId);
        //1.已注册且授权
        if (Objects.nonNull(user) && StrUtil.isNotBlank(user.getAvatar())){
            //说明已经注册并授权过了（只有授权过的用户才能查询到名字和头像）
            // 走登陆成功的逻辑，通过code找到给channel推送消息
            webSocketService.scanLoginSuccess(code,user.getId());
            // todo 这里暂时返回null
            return null;
        }
        //2.用户未注册，那就先注册。
        if(Objects.isNull(user)){
            User u=new User();
            u.setOpenId(openId);
            //注册完后数据库的用户信息只有 openId
            userService.register(u);
        }

        //注册临时关系 ，openId 和 code
        WAIT_AUTHORIZE_MAP.put(openId,code);

        //注册授权期间，服务器需要时间处理，可以先给前端发送一个ws信息：正在授权中
        webSocketService.waitAuthorize(code);

        //3.剩下的是已注册完，但是没有授权！没有授权就拿不到用户的头像和名字等信息。那就推送链接让用户授权
        //构建URL，拼接串。
        // 其中第二个参数为地址就是WxPortalController中的一个接口，靠这个重定向接口获取code
        String formatURL=String.format(URL,wxMpService.getWxMpConfigStorage().getAppId(),
                URLEncoder.encode(callBack+"/wx/portal/public/callBack"),
                "snsapi_userinfo");
        //返回一些提示信息。
        return TextBuilder.build("你好，语界欢迎您的回归。<a href=\""+formatURL+"\">请登录</a>"
                ,wxMpXmlMessage,wxMpService);
    }

    //获取code，对code处理
    private Integer getEventKey(WxMpXmlMessage wxMpXmlMessage) {
        //处理事件码是 qrscene_2的情况。有这个事件码就说明是新用户。
        try{
            String eventKey = wxMpXmlMessage.getEventKey();
            String code = eventKey.replace("qrscene_", "");
            return Integer.valueOf(code);
        }catch (Exception e){
            log.error("WXMsgServiceImpl.getEventKey()===>{}",e.getMessage());
            return null;
        }
    }

    /**
     * 1、拿到用户信息后，授权，保存用户的信息到数据库。
     * 2、认证完后，走登录成功的逻辑
     * @param userInfo
     */
    @Override
    public void authorize(WxOAuth2UserInfo userInfo) {
        String openId=userInfo.getOpenid();
        //通过openId，更新用户的信息，补上名字和头像等
        User user = userDao.getByOpenId(openId);
        if (StrUtil.isBlank(user.getAvatar())){
            User u=UserAdapter.buildAuthorizeUser(user.getId(),userInfo);
            //数据库中设置的name唯一，所以这里可能会出现名字重复(虽然概率很低)
            try{
                userDao.updateById(u);
            }catch (DuplicateKeyException e){
                log.info("WXMsgServiceImpl.authorize===>user插入数据库，name重复。{}",e.getMessage());
                //设置uuid
                u.setName(RandomUtil.randomString(10));
                userDao.updateById(u);
            }
        }

        //通过openId找到code
        Integer code = WAIT_AUTHORIZE_MAP.remove(openId);
        //将code传给  登陆成功的方法逻辑
        webSocketService.scanLoginSuccess(code, user.getId());
    }
}
